/*
 * REFSocketEventContext.h
 *
 *  Created on: Jun 10, 2013
 *      Author: Lucifer
 */

#ifndef __BFX_REFSOCKETEVENTCONTEXT_H__
#define __BFX_REFSOCKETEVENTCONTEXT_H__

#include "REFIOEventContext.h"

namespace BFX
{

#ifdef	BFX_IO_HAS_IOCP

////////////////////////////////////////////////////////////////////////////////

class REFSocketEventContext : public REFIOEventContext
{
public:
	REFSocketEventContext(REFSocket* pSocket, IOEventCallback callback)
		: REFIOEventContext(callback)
	{
		m_pSocket = pSocket;
	}

	REFSocket* GetSocket() const
	{
		return m_pSocket;
	}

	void SetSocketError(SocketError socketError)
	{
		m_nErrorCode = SocketException::TranslateSocketError(socketError);
	}
	SocketError GetSocketError() const
	{
		return SocketException::TranslateErrorCode(m_nErrorCode);
	}


protected:
	REF<REFSocket>	m_pSocket;
};

////////////////////////////////////////////////////////////////////////////////
// REFSocketAcceptEventContext

class REFSocketAcceptEventContext : public REFSocketEventContext
{
public:
	REFSocketAcceptEventContext(REFSocket* pListenSocket, REFSocket* pAcceptSocket, IOEventCallback callback)
		: REFSocketEventContext(pListenSocket, callback)
	{
		m_pAcceptSocket = pAcceptSocket;
	}

	REFSocket* GetAcceptSocket() const
	{
		return m_pAcceptSocket;
	}

	BYTE* GetBuffer() const
	{
		return (BYTE*)m_buffer;
	}
	int GetAddressLength() const
	{
		return (sizeof(SOCKADDR_STORAGE_LH) + 16);
	}

	virtual void Complete(int nErrorCode, int nBytesTransferred)
	{
		// map Windows error ERROR_NETNAME_DELETED to WSAECONNABORTED.
		if (nErrorCode == ERROR_NETNAME_DELETED)
			nErrorCode = WSAECONNABORTED;
		m_nErrorCode = nErrorCode;

		if (m_nErrorCode == 0)
		{
			//
			// Get the address of the accept socket.
			//
			LPSOCKADDR pLocalSockAddr = NULL;
			int nLocalSockAddrLen = 0;
			LPSOCKADDR pRemoteSockAddr = NULL;
			int nRemoteSockAddrLen = 0;
			::GetAcceptExSockaddrs(GetBuffer(), 0, GetAddressLength(), GetAddressLength(), &pLocalSockAddr,
					&nLocalSockAddrLen, &pRemoteSockAddr, &nRemoteSockAddrLen);

			REF<REFSocketAddress> pRemoteAddress = m_pSocket->getLocalAddress()->Create(pRemoteSockAddr, nRemoteSockAddrLen);

			//
			// Need to set the SO_UPDATE_ACCEPT_CONTEXT option so that getsockname and getpeername will work on the accepted socket.
			//
			SOCKET hAcceptSocket = m_pAcceptSocket->GetFileDescriptor();
			SOCKET hListenSocket = m_pSocket->GetFileDescriptor();
			::setsockopt(hAcceptSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&hListenSocket, sizeof(SOCKET));

			//
			// If the socket was successfully accepted, update inherited state.
			//
			m_pSocket->UpdateAcceptSocket(m_pAcceptSocket, pRemoteAddress);
		}

		//
		// invoke the IO event callback.
		//
		m_callback(this);
	}

private:

	REF<REFSocket>	m_pAcceptSocket;
	BYTE	m_buffer[(sizeof(SOCKADDR_STORAGE_LH) + 16) * 2];
};

////////////////////////////////////////////////////////////////////////////////

class REFSocketReceiveEventContext : public REFSocketEventContext
{
public:
	REFSocketReceiveEventContext(REFSocket* pSocket, IOEventCallback callback, BYTE* pBuffer, int iOffset, int nSize,
			SocketFlags socketFlags)
		: REFSocketEventContext(pSocket, callback)
	{
		m_pBuffer = pBuffer;
		m_iOffset = iOffset;
		m_nSize = nSize;
		m_socketFlags = socketFlags;
		m_nBytesTransferred	= 0;
	}

	virtual void Complete(int nErrorCode, int nBytesTransferred)
	{
		// map Windows error ERROR_NETNAME_DELETED.
		if (nErrorCode == ERROR_NETNAME_DELETED)
		{
			// TODO: set error code to operation aborted or connection reseted.
		}
		else if (nErrorCode == ERROR_PORT_UNREACHABLE)
		{
			nErrorCode = WSAECONNREFUSED;
		}
		m_nErrorCode = nErrorCode;

		// check for connection closed.
		if (m_nErrorCode == 0 && nBytesTransferred == 0 && m_pSocket->GetSocketType() == SocketType_Stream)
		{
			m_nErrorCode = WSAEHOSTDOWN;
		}

		m_nBytesTransferred = nBytesTransferred;

		//
		// invoke the IO event callback.
		//
		m_callback(this);
	}

	int GetByteTransferred() const
	{
		return m_nBytesTransferred;
	}

private:
	BYTE*	m_pBuffer;
	int		m_iOffset;
	int		m_nSize;
	SocketFlags	m_socketFlags;
	int		m_nBytesTransferred;
};

#else	//	BFX_IO_HAS_IOCP

////////////////////////////////////////////////////////////////////////////////
// REFSocketEventContext

class REFSocketEventContext : public REFIOEventContext
{
public:
	REFSocketEventContext(REFSocket* pSocket, IOEventCallback callback)
		: REFIOEventContext(callback)
	{
		m_pSocket = pSocket;
	}

	void Complete()
	{
		m_callback(this);
	}

	REFSocket* GetSocket() const
	{
		return m_pSocket;
	}

	void SetSocketError(SocketError socketError)
	{
		m_nErrorCode = SocketException::TranslateSocketError(socketError);
	}
	SocketError GetSocketError() const
	{
		return SocketException::TranslateErrorCode(m_nErrorCode);
	}

protected:
	REF<REFSocket>	m_pSocket;
};

////////////////////////////////////////////////////////////////////////////////
// REFSocketAcceptEventContext

class REFSocketAcceptEventContext : public REFSocketEventContext
{
public:
	REFSocketAcceptEventContext(REFSocket* pSocket, IOEventCallback callback)
		: REFSocketEventContext(pSocket, callback)
	{
	}

	virtual bool Perform()
	{
		try
		{
			// accept the waiting connection.
			m_pAcceptSocket = m_pSocket->Accept();

			// check if operation succeeded.
			if (m_pAcceptSocket != NULL)
				return true;

			SocketError socketError = m_pSocket->GetLastError();
			SetSocketError(socketError);

			// check if we need to run the operation again.
			if (socketError == SocketError_WouldBlock)
			{
				return false;
			}
			// TODO: retry operation if interrupted by signal.

			return true;
		}
		catch (const Exception& e)
		{
			SetSocketError(m_pSocket->GetLastError());
			BFX_DBGWARN_("IO")("exception: %s, message: %s\n", e.GetExceptionName(), e.GetMessage());

			return true;
		}
	}

	REFSocket* GetAcceptSocket() const
	{
		return m_pAcceptSocket;
	}

protected:
	REF<REFSocket>	m_pAcceptSocket;
};

////////////////////////////////////////////////////////////////////////////////
// REFSocketReceiveEventContext

class REFSocketReceiveEventContext : public REFSocketEventContext
{
public:
	REFSocketReceiveEventContext(REFSocket* pSocket, IOEventCallback callback, BYTE* pBuffer, int iOffset, int nSize,
			SocketFlags socketFlags)
		: REFSocketEventContext(pSocket, callback)
	{
		m_pBuffer = pBuffer;
		m_iOffset = iOffset;
		m_nSize = nSize;
		m_socketFlags = socketFlags;
		m_nBytesTransferred	= 0;
	}

	// @returns True if it's finished; otherwise false, which means need to run the operation again.
	virtual bool Perform()
	{
		// the socket is not connected.
		if (!m_pSocket->IsConnected())
		{
			SetSocketError(SocketError_NotConnected);
			return true;
		}

		SocketError socketError = SocketError_UnspecifiedError;

		// receive some data
		m_nBytesTransferred = m_pSocket->Receive(m_pBuffer, m_iOffset, m_nSize, m_socketFlags);

		// check if operation succeeded.
		if (m_nBytesTransferred > 0)
		{
			socketError = SocketError_Success;
		}
		else if (m_nBytesTransferred == SOCKET_ERROR)
		{
			socketError = m_pSocket->GetLastError();
		}
		// check for EOF (remote host closed).
		else if (m_nBytesTransferred == 0 && m_pSocket->GetSocketType() == SocketType_Stream)
		{
			socketError = SocketError_HostDown;
		}
		SetSocketError(socketError);

		// check if we need to run the operation again.
		if (socketError == SocketError_WouldBlock)
		{
			return false;
		}
		// TODO: retry operation if interrupted by signal.

		return true;
	}
	int GetByteTransferred() const
	{
		return m_nBytesTransferred;
	}
protected:
	BYTE*	m_pBuffer;
	int		m_iOffset;
	int		m_nSize;
	SocketFlags	m_socketFlags;
	int		m_nBytesTransferred;
};

#endif	//	BFX_IO_HAS_IOCP

} /* namespace BFX */

#endif /* __BFX_REFSOCKETEVENTCONTEXT_H__ */
